// Copyright (c) 2023 Huawei Device Co., Ltd.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::{error::*, grammar::*, structure::*, ParseError, Utf8Reader, XmlError};

// Get `UnexpectedCharacter` Error.
macro_rules! unexpected_character {
    ($reader:expr, $current:expr) => {{
        let position = $reader.position();
        Err(ParseError::UnexpectedCharacter(position.line, position.offset, $current).into())
    }};
}

// Get `UnexpectedEndOfXml` Error.
macro_rules! unexpected_eof {
    ($reader:expr) => {
        Err(ParseError::UnexpectedEndOfXml($reader.position().line).into())
    };
}

// Consecutively match the next series of characters. Match and skip all
// whitespace characters if matching a non-whitespace character.
macro_rules! eat_ws {
    ($reader:expr) => {
        loop {
            match $reader.peek()? {
                Some(' ' | '\t' | '\n' | '\r') => $reader.discard(),
                _ => break,
            }
        }
    };
}

// Consecutively match the next series of characters, match any number of
// whitespace characters until a non-whitespace character is matched,
// and return the number of whitespace characters matched.
macro_rules! eat_and_cnt_ws {
    ($reader:expr) => {{
        let mut cnt = 0;
        loop {
            match $reader.peek()? {
                Some(' ' | '\t' | '\n' | '\r') => {
                    $reader.discard();
                    cnt += 1;
                }
                _ => break,
            }
        }
        cnt
    }};
}

// Peek the next character and compare it with a specified character.
// Return an error if they do not match.
macro_rules! eat_char {
    ($reader:expr, $char:expr) => {
        match $reader.peek()? {
            Some(ch) if ch == $char => $reader.discard(),
            Some(x) => return unexpected_character!($reader, x),
            None => return unexpected_eof!($reader),
        }
    };
}

// Peek the next series of characters and compare them with a specified string.
// Return an error if they do not match.
macro_rules! eat_str {
    ($reader:expr, $str:expr) => {
        for item in $str.chars() {
            match $reader.peek()? {
                Some(ch) if ch == item => $reader.discard(),
                Some(x) => return unexpected_character!($reader, x),
                None => return unexpected_eof!($reader),
            }
        }
    };
}

// document    ::=   	prolog element Misc*
// prolog      ::=   	XMLDecl? Misc* (doctypedecl Misc*)?
// Misc	       ::=   	Comment | PI | S
pub(crate) fn parse_document<R: Utf8Reader>(reader: &mut R) -> Result<Document, XmlError> {
    // Create a result document.
    let mut document = Document::new();

    // Read starting whitespace characters. Make sure the next character is not
    // empty.
    eat_ws!(reader);

    // There are the following branches:
    // 1. Begin with '<' and '?' to indicate an `XmlDecl` section.
    // 2. Begin with '<' and '!' to indicate an `Comment` section.
    // 3. Begin with '<' and one other character to indicate an `Element` section.

    // In either case we need to match '<'.
    eat_char!(reader, '<');

    // If it matches '?', then go to the `XmlDecl` section.
    if reader.peek()? == Some('?') {
        reader.discard();

        // The matching range of function `parse_xml_decl`: [xml .. S?], Leading
        // '<?' and trailing '?>' are not included.
        parse_xml_decl(reader, &mut document)?;
        // Read the trailing '?>'.
        eat_str!(reader, "?>");

        // Back to `init` state.
        eat_ws!(reader);
        eat_char!(reader, '<');
    }

    // skip Comment, DTD, PIS
    loop {
        if reader.peek()? != Some('!') && reader.peek()? != Some('?') {
            break;
        }
        skip_unsupported_struct(reader)?;

        // Read the trailing '>'.
        eat_char!(reader, '>');

        // Back to `init` state.
        eat_ws!(reader);
        eat_char!(reader, '<');
    }

    // Document must contain exact one element.
    parse_document_element(reader, &mut document)?;

    eat_ws!(reader);

    // If "<!" is matched again here, it means that there are follow-up comments.
    while reader.peek()? == Some('<') {
        // Match "<!".
        reader.discard();
        skip_unsupported_struct(reader)?;
        eat_char!(reader, '>');
        // Match the trailing whitespace characters.
        eat_ws!(reader);
    }

    // If there are still non-blank characters here, return error.
    if reader.peek()?.is_some() {
        return Err(ParseError::TrailingBytes.into());
    }
    Ok(document)
}

// XMLDecl	   ::=   	'<?xml' VersionInfo EncodingDecl? SDDecl? S? '?>'
// The matching range of this function: [xml, S?].
fn parse_xml_decl<R: Utf8Reader>(reader: &mut R, document: &mut Document) -> Result<(), XmlError> {
    // '?' has been matched, so we just need to match "xml" here.
    eat_str!(reader, "xml");

    // There must be at least one whitespace character between `?xml` and
    // `VersionInfo`.
    match reader.peek()? {
        Some(' ' | '\t' | '\n' | '\r') => reader.discard(),
        Some(x) => return unexpected_character!(reader, x),
        None => return unexpected_eof!(reader),
    }
    eat_ws!(reader);

    // Match `VersionInfo` section.
    parse_version_info(reader, document)?;

    // If there is no blank character here, it can only be followed by '?>',
    // because `VersionInfo` `EncodingDecl` and `SDDecl` must be separated by at
    // least one whitespace character.
    if eat_and_cnt_ws!(reader) == 0 {
        return Ok(());
    }

    // If it matches `e`, it means there is an `Encoding` field.
    if reader.peek()? == Some('e') {
        // Match `EncodingDecl` section.
        parse_encoding_decl(reader, document)?;

        // If there is no blank character here, it can only be followed by '?>'.
        if eat_and_cnt_ws!(reader) == 0 {
            return Ok(());
        }
    }

    // If it matches `s`, it means there is an `Standalone` field.
    if reader.peek()? == Some('s') {
        // Match `SDDecl` section.
        parse_sd_decl(reader, document)?;

        // Match the trailing whitespace characters.
        eat_ws!(reader);
    }

    Ok(())
}

// VersionInfo	    ::=   	S 'version' Eq ("'" VersionNum "'" | '"' VersionNum '"')
// Eq               ::=   	S? '=' S?
fn parse_version_info<R: Utf8Reader>(
    reader: &mut R,
    document: &mut Document,
) -> Result<(), XmlError> {
    // VersionNum	   ::=   	'1.' [0-9]+
    // The matching range of this function: ['1.', [0-9]+].
    fn parse_version_num<R: Utf8Reader>(reader: &mut R) -> Result<String, XmlError> {
        reader.bytes_start();

        eat_str!(reader, "1.");

        // At least 1 number character must be matched here.
        match reader.peek()? {
            Some(ch) if ch.is_ascii_digit() => reader.discard(),
            Some(x) => return unexpected_character!(reader, x),
            None => return unexpected_eof!(reader),
        }

        // Match as many consecutive numeric characters as possible.
        loop {
            match reader.peek()? {
                Some(ch) if ch.is_ascii_digit() => reader.discard(),
                _ => break,
            }
        }

        // Safety: The characters have been judged when reading in, so we can use the
        // unchecked method.
        let version = unsafe { String::from_utf8_unchecked(reader.bytes_end().to_vec()) };
        Ok(version)
    }

    // Leading whitespace characters have been matched.
    eat_str!(reader, "version");

    // `S?` `=` `S?`
    eat_ws!(reader);
    eat_char!(reader, '=');
    eat_ws!(reader);

    // ("'" VersionNum "'" | '"' VersionNum '"')
    return match reader.peek()? {
        Some(mark @ ('\'' | '\"')) => {
            reader.discard();

            document.inner.borrow_mut().decl.version = parse_version_num(reader)?;

            // The front and back marks need to be consistent.
            eat_char!(reader, mark);
            Ok(())
        }
        Some(x) => unexpected_character!(reader, x),
        None => unexpected_eof!(reader),
    };
}

// EncodingDecl	   ::=   	S 'encoding' Eq ('"' EncName '"' | "'" EncName "'" )
// Eq              ::=   	S? '=' S?
fn parse_encoding_decl<R: Utf8Reader>(
    reader: &mut R,
    document: &mut Document,
) -> Result<(), XmlError> {
    // TODO: Support other encoding.
    // EncName	   ::=   	[A-Za-z] ([A-Za-z0-9._] | '-')*
    fn parse_enc_name<R: Utf8Reader>(reader: &mut R) -> Result<Encoding, XmlError> {
        // Currently only support utf8.
        match reader.peek()? {
            Some('u') => eat_str!(reader, "utf-8"),
            Some('U') => eat_str!(reader, "UTF-8"),
            Some(x) => return unexpected_character!(reader, x),
            None => return unexpected_eof!(reader),
        }
        Ok(Encoding::Utf8)
    }

    // Leading whitespace characters have been matched.
    eat_str!(reader, "encoding");

    // `S?` `=` `S?`
    eat_ws!(reader);
    eat_char!(reader, '=');
    eat_ws!(reader);

    // ('"' EncName '"' | "'" EncName "'" )
    return match reader.peek()? {
        Some(mark @ ('\'' | '\"')) => {
            reader.discard();

            document.inner.borrow_mut().decl.xml_encoding = Some(parse_enc_name(reader)?);

            // The front and back marks need to be consistent.
            eat_char!(reader, mark);
            Ok(())
        }
        Some(x) => unexpected_character!(reader, x),
        None => unexpected_eof!(reader),
    };
}

// SDDecl	   ::=   	S 'standalone' Eq (("'" ('yes' | 'no') "'") | ('"' ('yes' |
// 'no') '"')) Eq          ::=   	S? '=' S?
fn parse_sd_decl<R: Utf8Reader>(reader: &mut R, document: &mut Document) -> Result<(), XmlError> {
    fn parse_standalone<R: Utf8Reader>(reader: &mut R) -> Result<bool, XmlError> {
        match reader.peek()? {
            Some('y') => {
                eat_str!(reader, "yes");
                Ok(true)
            }
            Some('n') => {
                eat_str!(reader, "no");
                Ok(false)
            }
            Some(x) => unexpected_character!(reader, x),
            None => unexpected_eof!(reader),
        }
    }

    // Leading whitespace characters have been matched.
    eat_str!(reader, "standalone");

    // `S?` `=` `S?`
    eat_ws!(reader);
    eat_char!(reader, '=');
    eat_ws!(reader);

    // (("'" ('yes' | 'no') "'") | ('"' ('yes' | 'no') '"'))
    return match reader.peek()? {
        Some(mark @ ('\'' | '\"')) => {
            reader.discard();
            document.inner.borrow_mut().decl.standalone = Some(parse_standalone(reader)?);

            eat_char!(reader, mark);
            Ok(())
        }
        Some(x) => unexpected_character!(reader, x),
        None => unexpected_eof!(reader),
    };
}

fn parse_comment_and_dtd<R: Utf8Reader>(reader: &mut R) -> Result<(), XmlError> {
    match reader.peek()? {
        Some('-') => parse_comment(reader),
        Some('D') => skip_xml_dtd(reader),
        Some(x) => unexpected_character!(reader, x),
        None => unexpected_eof!(reader),
    }
}

// Comment	   ::=   	'<!--' ((Char - '-') | ('-' (Char - '-')))* '-->'
// Char        ::=      #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] |
// [#x10000-#x10FFFF]
fn parse_comment<R: Utf8Reader>(reader: &mut R) -> Result<(), XmlError> {
    // `!` has been matched.
    eat_str!(reader, "--");

    let mut minus_cnt = 0;
    loop {
        match reader.peek()? {
            Some('-') => {
                reader.discard();
                minus_cnt += 1;

                // Return if we match two consecutive '-'.
                if minus_cnt == 2 {
                    return Ok(());
                }
            }
            Some(x) if is_char(x) => {
                reader.discard();
                minus_cnt = 0;
            }
            Some(x) => return unexpected_character!(reader, x),
            None => return unexpected_eof!(reader),
        }
    }
}

// element	   ::=   	EmptyElemTag | STag content ETag
fn parse_document_element<R: Utf8Reader>(
    reader: &mut R,
    document: &mut Document,
) -> Result<(), XmlError> {
    // '<' has been read.
    let name = parse_name(reader)?;
    let mut element = Element::new_unchecked(String::new(), Some(document.downgrade()));

    // Parse the components of element.
    parse_element_parts(reader, name, &mut element)?;

    // Insert element into document.
    let mut document_inner = document.inner.borrow_mut();
    document_inner.document_element = Some(element.clone());
    let mut list = document_inner.children.list.borrow_mut();
    list.push(ChildNode::Element(element));
    Ok(())
}

// Name	   ::=   	NameStartChar (NameChar)*
fn parse_name<R: Utf8Reader>(reader: &mut R) -> Result<String, XmlError> {
    reader.bytes_start();

    // At least 1 character needs to be matched here.
    match reader.peek()? {
        Some(ch) if is_name_start_char(ch) => reader.discard(),
        Some(x) => return unexpected_character!(reader, x),
        None => return unexpected_eof!(reader),
    }

    loop {
        match reader.peek()? {
            Some(x) if is_name_char(x) => reader.discard(),
            Some(_) => break,
            None => return unexpected_eof!(reader),
        }
    }

    // Safety: The characters have been judged when reading in, so we can use the
    // unchecked method.
    let string = unsafe { String::from_utf8_unchecked(reader.bytes_end().to_vec()) };
    Ok(string)
}

// content	   ::=   	CharData? ((element | Reference | CDSect | PI | Comment)
// CharData?)*
fn parse_content<R: Utf8Reader>(reader: &mut R, element: &mut Element) -> Result<(), XmlError> {
    enum State {
        IN, // Initial state.
        LT, // '<' state.
        EX, // '<!' state.
        SK, // '<?' state.
    }

    // Leading whitespace characters need to be filtered.
    eat_ws!(reader);

    // Set initial state.
    let mut state = State::IN;

    loop {
        match (&mut state, reader.peek()?) {
            // In the `IN` state, if it matches '<', it will enter the `LT` state.
            (State::IN, Some('<')) => {
                reader.discard();
                state = State::LT;
            }
            (State::IN, Some(_)) => parse_text(reader, element)?,
            // In the `LT` state, if it matches '!', it will enter the `EX` state.
            (State::LT, Some('!')) => {
                reader.discard();
                state = State::EX;
            }
            (State::LT, Some('/')) => {
                reader.discard();
                return Ok(());
            }
            (State::LT, Some('?')) => {
                reader.discard();
                state = State::SK;
            }
            (State::LT, Some(_)) => {
                parse_child_element(reader, element)?;
                // Back to `IN` state.
                eat_ws!(reader);
                state = State::IN;
            }
            // In the `EX` state, if it matches '-', then try to match comment.
            (State::EX, Some('-')) => {
                parse_comment(reader)?;
                eat_char!(reader, '>');
                // Back to `IN` state.
                eat_ws!(reader);
                state = State::IN;
            }
            (State::EX, Some('D')) => {
                skip_xml_dtd(reader)?;
                eat_char!(reader, '>');
                // Back to `IN` state.
                eat_ws!(reader);
                state = State::IN;
            }
            // In the `EX` state, if it matches '[', then try to match CDATA section.
            (State::EX, Some('[')) => {
                parse_cdata_section(reader, element)?;
                eat_char!(reader, '>');
                // Back to `IN` state.
                eat_ws!(reader);
                state = State::IN;
            }
            (State::EX, Some(x)) => return unexpected_character!(reader, x),
            (State::SK, Some(_)) => {
                skip_xml_pis(reader)?;
                eat_char!(reader, '>');
                // Back to `IN` state.
                eat_ws!(reader);
                state = State::IN;
            }
            (_, None) => return unexpected_eof!(reader),
        }
    }
}

// CharData	   ::=   	[^<&]* - ([^<&]* ']]>' [^<&]*)
fn parse_text<R: Utf8Reader>(reader: &mut R, element: &mut Element) -> Result<(), XmlError> {
    // `val` is used to save the result.
    let mut val = String::new();

    // `ws` is used to identify whether the leading string is a continuous
    // whitespace string.
    let mut ws = false;

    reader.bytes_start();

    // Deal with the following situation here:
    // 1. Starting whitespace characters and ending whitespace characters will
    // not be used as the content of text.
    // 2. Substrings starting with & need to be escaped.
    loop {
        match reader.peek()? {
            Some('&') => {
                // If '&' is matched, try to add leading characters to the result.
                ws = false;
                unsafe {
                    val.as_mut_vec().extend_from_slice(reader.bytes_end());
                }
                parse_reference(reader, &mut val)?;
                reader.bytes_start();
            }
            Some('<') => {
                if ws {
                    reader.bytes_end();
                } else {
                    unsafe {
                        val.as_mut_vec().extend_from_slice(reader.bytes_end());
                    }
                }

                // Add `text` to element.
                let element_inner = element.inner.borrow();
                let mut text = Text::new_unchecked(val, element_inner.owner_document.clone());
                text.set_parent(Some(element.as_parent_node().downgrade()));
                let mut list = element_inner.children.list.borrow_mut();
                list.push(ChildNode::Text(text));
                return Ok(());
            }
            Some(' ' | '\t' | '\n' | '\r') => {
                // If whitespace is matched, try to add leading characters to the result.
                if !ws {
                    unsafe {
                        val.as_mut_vec().extend_from_slice(reader.bytes_end());
                    }
                    // Set `ws` to true.
                    ws = true;
                    reader.bytes_start();
                }
                reader.discard();
            }
            Some(_) => {
                ws = false;
                reader.discard();
            }
            None => return unexpected_eof!(reader),
        }
    }
}

// CDSect	   ::=   	CDStart CData CDEnd
fn parse_cdata_section<R: Utf8Reader>(
    reader: &mut R,
    element: &mut Element,
) -> Result<(), XmlError> {
    enum State {
        IN, // Initial state.
        FI, // ']' state.
        SE, // ']]' state.
    }

    eat_str!(reader, "[CDATA[");

    let mut value = String::new();
    reader.bytes_start();
    let mut state = State::IN;

    // The following situations need to be dealt with here:
    // 1. ']]>' will be treated as a terminator.
    // 2. ']' ']]' ']]]' ']]]]' ... are allowed to appear in the content of CDATA
    // section.
    loop {
        match (&mut state, reader.peek()?) {
            // Save the leading result string into value when we first match ']'.
            (State::IN, Some(']')) => {
                unsafe { value.as_mut_vec().extend_from_slice(reader.bytes_end()) };
                reader.discard();
                state = State::FI;
            }
            (State::IN, Some(_)) => reader.discard(),
            (State::FI, Some(']')) => {
                reader.discard();
                state = State::SE;
            }
            (State::FI, Some(_)) => {
                value.push(']');
                reader.bytes_start();
                reader.discard();
                state = State::IN;
            }
            (State::SE, Some('>')) => break,
            (State::SE, Some(']')) => {
                value.push(']');
                reader.discard();
            }
            (State::SE, Some(_)) => {
                value.push_str("]]");
                reader.bytes_start();
                reader.discard();
                state = State::IN;
            }
            (_, None) => return unexpected_eof!(reader),
        }
    }

    // Create a CDATA section, and add it to element.
    let element_inner = element.inner.borrow();
    let cdata_section = CdataSection::new_unchecked(value, element_inner.owner_document.clone());
    element_inner
        .children
        .list
        .borrow_mut()
        .push(ChildNode::CdataSection(cdata_section));
    Ok(())
}

fn parse_child_element<R: Utf8Reader>(
    reader: &mut R,
    element: &mut Element,
) -> Result<(), XmlError> {
    // '<' has been read.
    let name = parse_name(reader)?;
    let mut child_element =
        Element::new_unchecked(String::new(), element.inner.borrow().owner_document.clone());

    parse_element_parts(reader, name, &mut child_element)?;

    let element_inner = element.inner.borrow();
    let mut list = element_inner.children.list.borrow_mut();
    list.push(ChildNode::Element(child_element.clone()));
    Ok(())
}

fn parse_element_parts<R: Utf8Reader>(
    reader: &mut R,
    name: String,
    element: &mut Element,
) -> Result<(), XmlError> {
    loop {
        eat_ws!(reader);

        match reader.peek()? {
            // EmptyElemTag
            Some('/') => {
                eat_str!(reader, "/>");
                element.inner.borrow_mut().name = name;
                return Ok(());
            }
            Some('>') => {
                reader.discard();
                break;
            }
            // Loop to get attributes.
            Some(_) => parse_attribute(reader, element)?,
            None => return unexpected_eof!(reader),
        }
    }

    parse_content(reader, element)?;

    // Verify `Name`.
    eat_str!(reader, name);
    element.inner.borrow_mut().name = name;

    eat_ws!(reader);
    eat_char!(reader, '>');
    Ok(())
}

// Attribute	   ::=   	Name Eq AttValue
fn parse_attribute<R: Utf8Reader>(reader: &mut R, element: &mut Element) -> Result<(), XmlError> {
    let name = parse_name(reader)?;

    // `S?` `=` `S?`
    eat_ws!(reader);
    eat_char!(reader, '=');
    eat_ws!(reader);

    return match reader.peek()? {
        Some(mark @ ('\'' | '\"')) => {
            reader.discard();

            let mut val = String::new();
            reader.bytes_start();
            loop {
                match reader.peek()? {
                    Some('<') => return unexpected_character!(reader, '<'),
                    Some(ch) if ch == mark => {
                        unsafe { val.as_mut_vec().extend_from_slice(reader.bytes_end()) };
                        reader.discard();
                        break;
                    }
                    Some('&') => {
                        unsafe { val.as_mut_vec().extend_from_slice(reader.bytes_end()) };
                        parse_reference(reader, &mut val)?;
                        reader.bytes_start();
                    }
                    Some(_) => reader.discard(),
                    None => return unexpected_eof!(reader),
                }
            }
            let owner_document = element.inner.borrow().owner_document.clone();
            let attribute = Attribute::new_unchecked(name, val, owner_document);
            element.set_attribute(attribute);
            Ok(())
        }
        Some(x) => unexpected_character!(reader, x),
        None => unexpected_eof!(reader),
    };
}

fn parse_reference<R: Utf8Reader>(reader: &mut R, string: &mut String) -> Result<(), XmlError> {
    fn get_decimal<R: Utf8Reader>(reader: &mut R) -> Result<u32, XmlError> {
        let mut num = 0u32;

        // At least one digit must be matched.
        match reader.peek()? {
            Some(ch @ '0'..='9') => {
                num = num
                    .checked_add(ch as u32 - '0' as u32)
                    .ok_or(ParseError::Overflowed)?
            }
            Some(x) => return unexpected_character!(reader, x),
            None => return unexpected_eof!(reader),
        }

        reader.discard();

        loop {
            match reader.peek()? {
                Some(ch @ '0'..='9') => {
                    num = num.checked_mul(10).ok_or(ParseError::Overflowed)?;
                    num = num
                        .checked_add(ch as u32 - '0' as u32)
                        .ok_or(ParseError::Overflowed)?;
                    reader.discard();
                }
                Some(';') => return Ok(num),
                Some(x) => return unexpected_character!(reader, x),
                None => return unexpected_eof!(reader),
            }
        }
    }

    fn get_hexadecimal<R: Utf8Reader>(reader: &mut R) -> Result<u32, XmlError> {
        let mut num = 0u32;

        // At least one digit must be matched.
        match reader.peek()? {
            Some(ch @ '0'..='9') => {
                num = num
                    .checked_add(ch as u32 - '0' as u32)
                    .ok_or(ParseError::Overflowed)?
            }
            Some(ch @ 'a'..='f') => {
                num = num
                    .checked_add(ch as u32 - 'a' as u32 + 10u32)
                    .ok_or(ParseError::Overflowed)?
            }
            Some(ch @ 'A'..='F') => {
                num = num
                    .checked_add(ch as u32 - 'A' as u32 + 10u32)
                    .ok_or(ParseError::Overflowed)?
            }
            Some(x) => return unexpected_character!(reader, x),
            None => return unexpected_eof!(reader),
        }

        reader.discard();

        loop {
            match reader.peek()? {
                Some(ch @ '0'..='9') => {
                    num = num.checked_mul(16u32).ok_or(ParseError::Overflowed)?;
                    num = num
                        .checked_add(ch as u32 - '0' as u32)
                        .ok_or(ParseError::Overflowed)?;
                    reader.discard();
                }
                Some(ch @ 'a'..='f') => {
                    num = num.checked_mul(16u32).ok_or(ParseError::Overflowed)?;
                    num.checked_add(ch as u32 - 'a' as u32 + 10u32)
                        .ok_or(ParseError::Overflowed)?;
                    reader.discard();
                }
                Some(ch @ 'A'..='F') => {
                    num = num.checked_mul(16u32).ok_or(ParseError::Overflowed)?;
                    num = num
                        .checked_add(ch as u32 - 'A' as u32 + 10u32)
                        .ok_or(ParseError::Overflowed)?;
                    reader.discard();
                }
                Some(';') => return Ok(num),
                Some(x) => return unexpected_character!(reader, x),
                None => return unexpected_eof!(reader),
            }
        }
    }

    eat_char!(reader, '&');
    match reader.peek()? {
        // &lt;
        Some('l') => {
            eat_str!(reader, "lt;");
            string.push('<');
        }
        // &gt;
        Some('g') => {
            eat_str!(reader, "gt;");
            string.push('>');
        }
        // &amp; &apos;
        Some('a') => {
            reader.discard();
            match reader.peek()? {
                Some('m') => {
                    eat_str!(reader, "mp;");
                    string.push('&');
                }
                Some('p') => {
                    eat_str!(reader, "pos;");
                    string.push('\'');
                }
                Some(x) => return unexpected_character!(reader, x),
                None => return unexpected_eof!(reader),
            }
        }
        // &quot;
        Some('q') => {
            eat_str!(reader, "quot;");
            string.push('\"');
        }
        // &#xxx;
        Some('#') => {
            reader.discard();
            let num = match reader.peek()? {
                Some('0'..='9') => get_decimal(reader)?,
                Some('x') => {
                    reader.discard();
                    get_hexadecimal(reader)?
                }
                Some(x) => return unexpected_character!(reader, x),
                None => return unexpected_eof!(reader),
            };
            eat_char!(reader, ';');
            let ch = match char::from_u32(num) {
                Some(ch) => ch,
                None => return Err(ParseError::InvalidUnicode.into()),
            };
            string.push(ch)
        }
        Some(x) => return unexpected_character!(reader, x),
        None => return unexpected_eof!(reader),
    }
    Ok(())
}

fn skip_xml_dtd<R: Utf8Reader>(reader: &mut R) -> Result<(), XmlError> {
    enum State {
        ST, // Has read "DOCTYPE"
        LT, // Has read '['
        RT, // Has read ']' and skip white space
    }

    eat_str!(reader, "DOCTYPE");
    let mut state = State::ST;

    loop {
        match (&mut state, reader.peek()?) {
            (State::ST, Some('[')) => {
                reader.discard();
                state = State::LT;
                println!("has read \'[\'");
            }
            (State::ST, Some(']')) => {
                return Err(XmlError::from(ParseError::IncorrectDTD(
                    DTDError::MissLeftSquareBracket,
                )));
            }
            (State::ST, Some('>')) => {
                break; // It may still be in the dtd section, if '[' is missing.
            }
            (State::ST, Some(_)) => reader.discard(),
            (State::LT, Some(']')) => {
                reader.discard();
                eat_ws!(reader);
                state = State::RT;
            }
            (State::LT, Some(_)) => reader.discard(),
            (State::LT, None) => {
                return Err(XmlError::from(ParseError::IncorrectDTD(
                    DTDError::MissRightSquareBracket,
                )));
            }
            (State::RT, Some('>')) => {
                break;
            }
            (State::RT, Some(x)) => return unexpected_character!(reader, x),
            (_, None) => return unexpected_eof!(reader),
        }
    }
    Ok(())
}

fn skip_xml_pis<R: Utf8Reader>(reader: &mut R) -> Result<(), XmlError> {
    // '<?' has been read
    enum State {
        IN, // initial state.
        EX, // '<?...?' state.
    }
    let mut state = State::IN;
    loop {
        match (&mut state, reader.peek()?) {
            (State::IN, Some('?')) => {
                reader.discard();
                state = State::EX;
            }
            (State::IN, Some(_)) => reader.discard(),
            (State::EX, Some('>')) => return Ok(()),
            (State::EX, Some(x)) => return unexpected_character!(reader, x),
            (_, None) => return unexpected_eof!(reader),
        }
    }
}

fn skip_unsupported_struct<R: Utf8Reader>(reader: &mut R) -> Result<(), XmlError> {
    match reader.peek()? {
        Some('!') => {
            reader.discard();
            parse_comment_and_dtd(reader)?;
        }
        Some('?') => {
            reader.discard();
            skip_xml_pis(reader)?;
        }
        _ => return Ok(()),
    }
    Ok(())
}

#[cfg(test)]
mod ut_parse {
    use crate::{
        parse::parse_document, DocumentMethod, ElementMethod, Encoding, NodeMethod,
        ParentNodeMethod, StrReader, ValuedNodeMethod,
    };

    /*
     * @title  UT test for err branch
     * @design Condition Coverage
     * @precon NA
     * @brief  1. Construct text
     *         2. Call `parse_document` method to parse it.
     *         3. Check the result.
     * @expect 1. false。
     * @auto   yes
     */
    #[test]
    fn ut_xml_version_branch_err() {
        let text = r#"<element attr='a&"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        let text = r#"<element attr='a&k"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        let text = r#"<element attr='a&"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        let text = r#"<element attr='a&#"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        let text = r#"<element attr='a&ax"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        let text = r#"<element attr='a&a"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        let text = r#"<element attr='a&#x1m"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        let text = r#"<element attr='a&#x0"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        let text = r#"<element attr='a&#x"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        let text = r#"<element attr='a&#0"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        let text = r#"<element attr='a&#0a"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        let text = r#"<element attr="#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        let text = r#"<element><"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        let text = r#"<"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        let text = r#"<!--"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        let text = r#"<?xml version="1.0" standalone="#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        let text = r#"<?xml version="1.0" standalone=""#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        let text = r#"<?xml version="1.0" encoding="#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        let text = r#"<?xml version="1.0" encoding=""#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        let text = r#"<?xml version=x"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        let text = r#"<?xml version="#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        let text = r#"<?xml version="1."#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        let text = r#"<?xml"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        let text = r#"<?xml version="1.a"?><element/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        let text = r#"<element attr='a&#e;&lt;&#e;b'/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        let text = r#"<?xml version='1.0' encoding=UTF-8' standalone='yes'?><element/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        let text = r#"<element><!CDATA[a&#x20;&lt;&#20;b]]></element>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        let text = r#"<element><![CDATA[a]></element>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        let text = r#"<element></element><!-- Comment -->abc"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());
    }

    /// UT test case for `XmlDecl`.
    ///
    /// # Brief
    /// 1. Construct various forms of `XmlDecl`.
    /// 2. Call `parse_document` method to parse it.
    /// 3. Check the result.
    #[test]
    fn ut_xml_decl() {
        // Version
        let text = r#"<?xml version="1.0"?><element/>"#;
        let mut reader = StrReader::new(text);
        let document = parse_document(&mut reader).unwrap();
        assert_eq!(document.version(), "1.0");
        assert_eq!(document.xml_encoding(), None);
        assert_eq!(document.standalone(), None);

        // Version + Encoding
        let text = r#"<?xml version="1.0" encoding="UTF-8"?><element/>"#;
        let mut reader = StrReader::new(text);
        let document = parse_document(&mut reader).unwrap();
        assert_eq!(document.version(), "1.0");
        assert_eq!(document.xml_encoding(), Some(Encoding::Utf8));
        assert_eq!(document.standalone(), None);

        // Version + Standalone
        let text = r#"<?xml version="1.0" standalone="yes"?><element/>"#;
        let mut reader = StrReader::new(text);
        let document = parse_document(&mut reader).unwrap();
        assert_eq!(document.version(), "1.0");
        assert_eq!(document.xml_encoding(), None);
        assert_eq!(document.standalone(), Some(true));

        // Version + Encoding + Standalone
        let text = r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?><element/>"#;
        let mut reader = StrReader::new(text);
        let document = parse_document(&mut reader).unwrap();
        assert_eq!(document.version(), "1.0");
        assert_eq!(document.xml_encoding(), Some(Encoding::Utf8));
        assert_eq!(document.standalone(), Some(true));

        // Version + Encoding + Standalone + '\''
        let text = r#"<?xml version='1.0' encoding='UTF-8' standalone='yes'?><element/>"#;
        let mut reader = StrReader::new(text);
        let document = parse_document(&mut reader).unwrap();
        assert_eq!(document.version(), "1.0");
        assert_eq!(document.xml_encoding(), Some(Encoding::Utf8));
        assert_eq!(document.standalone(), Some(true));

        // Version + Encoding + Standalone + Extra Whitespace.
        let text = r#"
            <?xml
               version       =        "1.0"           encoding
                =         "UTF-8"              standalone                =
                "yes"
            ?><element/>
        "#;
        let mut reader = StrReader::new(text);
        let document = parse_document(&mut reader).unwrap();
        assert_eq!(document.version(), "1.0");
        assert_eq!(document.xml_encoding(), Some(Encoding::Utf8));
        assert_eq!(document.standalone(), Some(true));

        // Error: No '<'.
        let text = r#"?xml version="1.0"?><element/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        // Error: No '?'.
        let text = r#"<xml version="1.0"?><element/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        // Error: No 'xml'.
        let text = r#"<? version="1.0"?><element/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        // Error: No ' '
        let text = r#"<?xmlversion="1.0"?><element/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        // Error: No 'version'
        let text = r#"<?xml ="1.0"?><element/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        // Error: No '='
        let text = r#"<?xml version"1.0"?><element/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        // Error: No '\"'
        let text = r#"<?xml version=1.0"?><element/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        // Error: '\'' and '\"'
        let text = r#"<?xml version='1.0"?><element/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        // Error: No 'VersionNum'
        let text = r#"<?xml version=""?><element/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        // Error: Invalid 'VersionNum'
        let text = r#"<?xml version="999.999"?><element/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        // Error: No '?'
        let text = r#"<?xml version="1.0"><element/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        // Error: No '>'
        let text = r#"<?xml version="1.0"?<element/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        // Error: No ' '
        let text = r#"<?xml version="1.0"encoding="UTF-8"?><element/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        // Error: No 'encoding'
        let text = r#"<?xml version="1.0" ="UTF-8"?><element/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        // Error: No '='
        let text = r#"<?xml version="1.0" encoding"UTF-8"?><element/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        // Error: No '\"'
        let text = r#"<?xml version="1.0"encoding=UTF-8"?><element/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        // Error: No 'EncName'
        let text = r#"<?xml version="1.0" encoding=""?><element/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        // Error: Invalid 'EncName'
        let text = r#"<?xml version="1.0" encoding="utf"?><element/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        // Error: No ' '
        let text = r#"<?xml version="1.0" encoding="UTF-8"standalone="yes"?><element/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        // Error: No 'standalone'
        let text = r#"<?xml version="1.0" encoding="UTF-8" ="yes"?><element/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        // Error: No '='
        let text = r#"<?xml version="1.0" encoding="UTF-8" standalone"yes"?><element/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        // Error: No '\"'
        let text = r#"<?xml version="1.0" encoding="UTF-8" standalone=yes"?><element/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        // Error: No 'yes' or 'no'
        let text = r#"<?xml version="1.0" encoding="UTF-8" standalone=""?><element/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        // Error: Invalid standalone content.
        let text = r#"<?xml version="1.0" encoding="UTF-8" standalone="abc"?><element/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        let text = r#"<?xml version="1.0" standalone="no"?><element/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_ok());

        let text = r#"<?xml version="1.0" encoding="UTF-8" standalone="no"?><element/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_ok());
    }

    /// UT test case for `Comment`.
    ///
    /// # Brief
    /// 1. Construct various forms of `Comment`.
    /// 2. Call `parse_document` method to parse it.
    /// 3. Check the result.
    #[test]
    fn ut_comment() {
        // Comment
        let text = r#"<!-- Comment --><element/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_ok());

        // Error: No '<'
        let text = r#"!-- Comment --><element/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        // Error: No '!'
        let text = r#"<-- Comment --><element/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        // Error: No '--'
        let text = r#"<! Comment --><element/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        let text = r#"<element></element><!-- Comment -->"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_ok());
    }

    /// UT test case for `Element`.
    ///
    /// # Brief
    /// 1. Construct various forms of `Element`.
    /// 2. Call `parse_document` method to parse it.
    /// 3. Check the result.
    #[test]
    fn ut_element() {
        // EmptyElemTag
        let text = r#"<element/>"#;
        let mut reader = StrReader::new(text);
        let document = parse_document(&mut reader).unwrap();
        let element = document.document_element().unwrap();
        assert_eq!(element.name(), "element");
        assert!(!element.has_children());
        assert!(!element.has_attributes());

        // EmptyElemTag + Attribute
        let text = r#"<element attr="a&#x20;&lt;&#x20;b"/>"#;
        let mut reader = StrReader::new(text);
        let document = parse_document(&mut reader).unwrap();
        let element = document.document_element().unwrap();
        assert_eq!(element.name(), "element");
        assert!(!element.has_children());
        assert!(element.has_attributes());
        let attributes = element.attributes();
        let attr = attributes.get("attr").unwrap();
        assert_eq!(attr.name(), "attr");
        assert_eq!(attr.value(), "a < b");

        // EmptyElemTag + Attribute + \'
        let text = r#"<element attr='a&#x20;&lt;&#x20;b'/>"#;
        let mut reader = StrReader::new(text);
        let document = parse_document(&mut reader).unwrap();
        let element = document.document_element().unwrap();
        assert_eq!(element.name(), "element");
        assert!(!element.has_children());
        assert!(element.has_attributes());
        let attributes = element.attributes();
        let attr = attributes.get("attr").unwrap();
        assert_eq!(attr.name(), "attr");
        assert_eq!(attr.value(), "a < b");

        // EmptyElemTag + Attribute + Extra Whitespace.
        let text = r#"<element           attr             =
        "a&#x20;&lt;&#x20;b"            />"#;
        let mut reader = StrReader::new(text);
        let document = parse_document(&mut reader).unwrap();
        let element = document.document_element().unwrap();
        assert_eq!(element.name(), "element");
        assert!(!element.has_children());
        assert!(element.has_attributes());
        let attributes = element.attributes();
        let attr = attributes.get("attr").unwrap();
        assert_eq!(attr.name(), "attr");
        assert_eq!(attr.value(), "a < b");

        // STag + ETag
        let text = r#"<element></element>"#;
        let mut reader = StrReader::new(text);
        let document = parse_document(&mut reader).unwrap();
        let element = document.document_element().unwrap();
        assert_eq!(element.name(), "element");
        assert!(!element.has_children());
        assert!(!element.has_attributes());

        // STag + ETag + Attribute
        let text = r#"<element attr="a&#x20;&lt;&#x20;b"></element>"#;
        let mut reader = StrReader::new(text);
        let document = parse_document(&mut reader).unwrap();
        let element = document.document_element().unwrap();
        assert_eq!(element.name(), "element");
        assert!(!element.has_children());
        assert!(element.has_attributes());
        let attributes = element.attributes();
        let attr = attributes.get("attr").unwrap();
        assert_eq!(attr.name(), "attr");
        assert_eq!(attr.value(), "a < b");

        // STag + ETag + Attribute + '\''
        let text = r#"<element attr='a&#x20;&lt;&#x20;b'></element>"#;
        let mut reader = StrReader::new(text);
        let document = parse_document(&mut reader).unwrap();
        let element = document.document_element().unwrap();
        assert_eq!(element.name(), "element");
        assert!(!element.has_children());
        assert!(element.has_attributes());
        let attributes = element.attributes();
        let attr = attributes.get("attr").unwrap();
        assert_eq!(attr.name(), "attr");
        assert_eq!(attr.value(), "a < b");

        // STag + ETag + Attribute + Whitespace
        let text = r#"<element          attr           =
        'a&#x20;&lt;&#x20;b'               ></element>"#;
        let mut reader = StrReader::new(text);
        let document = parse_document(&mut reader).unwrap();
        let element = document.document_element().unwrap();
        assert_eq!(element.name(), "element");
        assert!(!element.has_children());
        assert!(element.has_attributes());
        let attributes = element.attributes();
        let attr = attributes.get("attr").unwrap();
        assert_eq!(attr.name(), "attr");
        assert_eq!(attr.value(), "a < b");

        // Whitespace content
        let text = r#"<element>              </element>"#;
        let mut reader = StrReader::new(text);
        let document = parse_document(&mut reader).unwrap();
        let element = document.document_element().unwrap();
        assert_eq!(element.name(), "element");
        assert!(!element.has_children());
        assert!(!element.has_attributes());

        // Text content
        let text = r#"<element>a&#x20;&lt;&#x20;b</element>"#;
        let mut reader = StrReader::new(text);
        let document = parse_document(&mut reader).unwrap();
        let element = document.document_element().unwrap();
        assert_eq!(element.name(), "element");
        assert!(element.has_children());
        assert!(!element.has_attributes());
        let children = element.children();
        let text = children.item(0).unwrap().into_text().unwrap();
        assert_eq!(text.value(), "a < b");

        // Text content + Whitespace
        let text = r#"<element>
                   a&#x20;&lt;&#x20;b
        </element>"#;
        let mut reader = StrReader::new(text);
        let document = parse_document(&mut reader).unwrap();
        let element = document.document_element().unwrap();
        assert_eq!(element.name(), "element");
        assert!(element.has_children());
        assert!(!element.has_attributes());
        let children = element.children();
        let text = children.item(0).unwrap().into_text().unwrap();
        assert_eq!(text.value(), "a < b");

        // CDATA content
        let text = r#"<element><![CDATA[a&#x20;&lt;&#20;b]]></element>"#;
        let mut reader = StrReader::new(text);
        let document = parse_document(&mut reader).unwrap();
        let element = document.document_element().unwrap();
        assert_eq!(element.name(), "element");
        assert!(element.has_children());
        assert!(!element.has_attributes());
        let children = element.children();
        let text = children.item(0).unwrap().into_cdata_section().unwrap();
        assert_eq!(text.value(), "a&#x20;&lt;&#20;b");

        // CDATA content + Whitespace
        let text = r#"<element>
                <![CDATA[a&#x20;&lt;&#20;b]]>
        </element>"#;
        let mut reader = StrReader::new(text);
        let document = parse_document(&mut reader).unwrap();
        let element = document.document_element().unwrap();
        assert_eq!(element.name(), "element");
        assert!(element.has_children());
        assert!(!element.has_attributes());
        let children = element.children();
        let text = children.item(0).unwrap().into_cdata_section().unwrap();
        assert_eq!(text.value(), "a&#x20;&lt;&#20;b");

        // Comment
        let text = r#"<element><!-- Comment --></element>"#;
        let mut reader = StrReader::new(text);
        let document = parse_document(&mut reader).unwrap();
        let element = document.document_element().unwrap();
        assert_eq!(element.name(), "element");
        assert!(!element.has_children());
        assert!(!element.has_attributes());

        // Comment + Whitespace
        let text = r#"<element>
                       <!-- Comment -->
        </element>"#;
        let mut reader = StrReader::new(text);
        let document = parse_document(&mut reader).unwrap();
        let element = document.document_element().unwrap();
        assert_eq!(element.name(), "element");
        assert!(!element.has_children());
        assert!(!element.has_attributes());

        // Element
        let text = r#"<element><child/></element>"#;
        let mut reader = StrReader::new(text);
        let document = parse_document(&mut reader).unwrap();
        let element = document.document_element().unwrap();
        assert_eq!(element.name(), "element");
        assert!(element.has_children());
        assert!(!element.has_attributes());
        let list = element.get_elements("child");
        let child_element = list.item(0).unwrap();
        assert_eq!(child_element.name(), "child");
        assert!(!child_element.has_children());
        assert!(!child_element.has_attributes());

        // Element + Whitespace
        let text = r#"<element>
                         <child/>
        </element>"#;
        let mut reader = StrReader::new(text);
        let document = parse_document(&mut reader).unwrap();
        let element = document.document_element().unwrap();
        assert_eq!(element.name(), "element");
        assert!(element.has_children());
        assert!(!element.has_attributes());
        let list = element.get_elements("child");
        let child_element = list.item(0).unwrap();
        assert_eq!(child_element.name(), "child");
        assert!(!child_element.has_children());
        assert!(!child_element.has_attributes());

        // Mixed
        let text = r#"<element>
            a&#x20;&lt;&#x20;b
            <!-- Comment -->
            <![CDATA[a&#x20;&lt;&#x20;b]]>
            <child/>
        </element>"#;
        let mut reader = StrReader::new(text);
        let document = parse_document(&mut reader).unwrap();
        let element = document.document_element().unwrap();
        assert_eq!(element.name(), "element");
        assert!(element.has_children());
        assert!(!element.has_attributes());
        let children = element.children();
        assert_eq!(children.len(), 3);
        let child1 = children.item(0).unwrap().into_text().unwrap();
        assert_eq!(child1.value(), "a < b");
        let child2 = children.item(1).unwrap().into_cdata_section().unwrap();
        assert_eq!(child2.value(), "a&#x20;&lt;&#x20;b");
        let child3 = children.item(2).unwrap().into_element().unwrap();
        assert_eq!(child3.name(), "child");
        assert!(!child3.has_children());
        assert!(!child3.has_attributes());

        // Error: No '<'
        let text = r#"element/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        // Error: No `Name`
        let text = r#"</>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        // Error: No '/>'
        let text = r#"<element"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        // Error: No ' '
        let text = r#"<elementattr="a&#x20;&lt;&#20;"/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        // Error: No `AttrName`
        let text = r#"<element ="a&#x20;&lt;&#20;"/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        // Error: No `=`
        let text = r#"<element attr"a&#x20;&lt;&#20;"/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        // Error: No `\"`
        let text = r#"<element attr=a&#x20;&lt;&#20;"/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        // Error: '\'' and '\"'
        let text = r#"<element attr='a&#x20;&lt;&#20;"/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        // Error: Invalid '<'
        let text = r#"<element attr='a&#x20;<&#20;"/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        // Error: Invalid '&'
        let text = r#"<element attr='a&#x20;&&#20;"/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        // Error: No `Number`
        let text = r#"<element attr='a&#x;&lt;&#20;"/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        let text = r#"<?xml version="1.0" encoding="UTF-8" standalone="no"?><element/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_ok());

        let text = r#"<!--123--><element/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_ok());

        let text = r#"<element><![CDATA[a&#x20;&]]lt;&#20;b]]></element>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_ok());

        let text = r#"<element><![CDATA[a]]]></element>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_ok());

        let text = r#"<element attr="a&#xae;&lt;&#xAE;b"/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_ok());
    }

    /// UT test case for `DTD`.
    ///
    /// # Brief
    /// 1. Construct various forms of `DTD`.
    /// 2. Call `parse_document` method to skip it.
    /// 3. Check the result.
    #[test]
    fn ut_xml_dtd() {
        // DTD
        let text = r#"<!DOCTYPE address
        [ <!ELEMENT address (name,company,phone)> ]>
        <element/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_ok());

        // DTD with file path.
        let text = r#"<!DOCTYPE note SYSTEM "Note.dtd"><element/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_ok());

        // Error: No '<'
        let text = r#"!DOCTYPE address
        [ <!ELEMENT address (name,company,phone)> ]>
        <element/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        // Error: No '!'
        let text = r#"<DOCTYPE address
        [ <!ELEMENT address (name,company,phone)> ]>
        <element/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        // Error: No '['
        let text = r#"<DOCTYPE address
         <!ELEMENT address (name,company,phone)> ]>
        <element/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        // Error: No ']'
        let text = r#"<DOCTYPE address
        [ <!ELEMENT address (name,company,phone)> >
        <element/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        // Error: No 'DOCTYPE'
        let text = r#"!EPYTCOD address
        [ <!ELEMENT address (name,company,phone)> ]>
        <element/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        let text = r#"<element/><!DOCTYPE address
        [ <!ELEMENT address (name,company,phone)> ]>
        "#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_ok());
    }

    /// UT test case for `PIS`.
    ///
    /// # Brief
    /// 1. Construct various forms of `PIS`.
    /// 2. Call `parse_document` method to skip it.
    /// 3. Check the result.
    #[test]
    fn ut_xml_pis() {
        // PIS
        let text = r#"<?xml version="1.0"?>
        <?welcome to pg=10 of NewWord?><element/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_ok());

        // Error: No '<'
        let text = r#"<?xml version="1.0"?>
        ?welcome to pg=10 of NewWord?><element/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        // Error: No '?'
        let text = r#"<?xml version="1.0"?>
        <welcome to pg=10 of NewWord?><element/>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_err());

        let text = r#"<?xml version="1.0"?>
        <element/><?welcome to pg=10 of NewWord?>"#;
        let mut reader = StrReader::new(text);
        assert!(parse_document(&mut reader).is_ok());
    }
}
